<?php

namespace paper;

use Exception;
use http\Exception\RuntimeException;
use paper\exception\ClassNotFoundException;
use paper\exception\BaseException;
use ReflectionException;
use ReflectionFunctionAbstract;

class Container
{

    private static ?Container $instance = null;
    private array $instantiations = [];

    private function __construct()
    {
    }

    public static function getInstance(): static
    {
        if (static::$instance == null) {
            static::$instance = new static();
        }
        return static::$instance;
    }

    public function put(string $key, $instantiation)
    {
        $this->instantiations[$key] = $instantiation;
    }

    public function singleton(string $key, $instantiation)
    {
        $this->instantiations[$key] = $instantiation;
    }

    /**
     * @param string $key
     * @return mixed
     */
    public function get(string $key): mixed
    {
        if (!isset($this->instantiations[$key])) {
            $this->instantiations[$key] = $this->make($key);
        }
        $instance = $this->instantiations[$key];
        if ($instance instanceof \Closure) {
            $instance = $this->instantiations[$key] = $instance($this);
        }
        return $instance;
    }




    public function test()
    {
//        $this->get(Request::class)
    }

//    public function get(string $abstract): mixed
//    {
//        //判断抽象接口是否已经存在，存在则直接返回
//        if (isset($this->instances[$abstract]))
//            return $this->instances[$abstract];
//
//        //判断抽象接口是否已经绑定，如果绑定则实例化
//        if (!isset($this->bind[$abstract])) {
//            throw new Exception("");
//        }
//        [$closure, $singleton] = $this->bind[$abstract];
//        $instance = $closure($this);
//        if ($singleton)
//            $this->instances[$abstract] = $instance;
//        return $instance;
//    }
//
    /**
     * @param string $abstract
     * @return mixed
     * @throws \RuntimeException
     */
    public function make(string $abstract): mixed
    {
//        if (1 == 2)
//            return new $abstract;//这一段用来欺骗IDE
        try {
            $ref = new \ReflectionClass($abstract);
            $constructor = $ref->getConstructor();
            if (!$constructor) {
                return $ref->newInstance();
            }
            if ($constructor->isPublic()) {
                $params = $this->bindParams($constructor);
                return $ref->newInstanceArgs($params);
            }
            throw new RuntimeException();
        } catch (ReflectionException $e) {
            throw new ClassNotFoundException($e->getMessage());
        }
    }

//


    private function bindParams(ReflectionFunctionAbstract $reflectionMethod): array
    {
        $parameters = [];
        foreach ($reflectionMethod->getParameters() as $parameter) {
            $parameterClass = $parameter->getType();
            if ($parameterClass) {
                $parameters[$parameter->name] = $this->make($parameterClass->getName());
            }
        }
        return $parameters;
    }
}